/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file clock_drv.c
 * @brief This file defines clock driver functions
 */

/*******Includes***************************************************************/
#include <stddef.h>
#ifdef CPU_IM90
#include "system_IM90.h"
#elif CPU_IM94
#include "system_IM94.h"
#else
#endif
#include "clock_reg_access.h"
#if (CONFIG_FUNCTION_SAFE == 1U)
#include "libsafety.h"
#endif

/*******Definitions************************************************************/
#define HSI_CLK_FREQ                (8000000U)  /*!< High Speed Internal RC frequency */
#define LSI_CLK_FREQ                (32000U)    /*!< Low Speed Internal RC frequency */
#define LSE_CLK_FREQ                (32768U)    /*!< Low Speed External Clock frequency */

#define HSE_READY_TIMEOUT           (0x20000U)  /*!< High Speed Internal RC ready timeout value */
#define PLL_READY_TIMEOUT           (0x20000U)  /*!< Phase-locked Loop ready timeout value */
#define CLOCK_REG_WIDTH             (32U)       /*!< Register width */

/*!
 * @brief PLL divider configurations
 */
typedef struct {
    uint32_t prediv : 6U;   /*!< PLL previous divider, 0~63 */
    uint32_t vcodiv : 1U;   /*!< PLL VCO divider, 0~1 */
    uint32_t postdiv1 : 3U; /*!< PLL post divider1, 0~7 */
    uint32_t postdiv2 : 2U; /*!< PLL post divider2, 0~3 */
    uint32_t fbdiv : 12U;   /*!< PLL feedback divider, 0~4095 */
} pll_div_t;

/*******Variables**************************************************************/
/*! @brief Global variable to save EXTAL/XTAL frequency */
static uint32_t s_hseFreq = 8000000U;

/*! @brief Global variable to save pll vco divider */
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
static uint16_t s_vcodiv = 1U;
#else
static uint16_t s_vcodiv = 0U;
#endif

/*! @brief Global variable for pll dividers */
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
static const pll_div_t s_pllDivConfig[VCO_FREQ_MAX][PLL_IN_MAX][PLL_OUT_MAX] = {
    /* prediv, vcodiv, postdiv1, postdiv2, fbdiv */
    /* VCO = 960MHz */
    {
        /* PLL_IN = 4MHz */
        {
            {0U, 0U, 2U, 1U, 235U}, /* PLL_OUT = 80MHz, VCO = 960MHz */
            {0U, 1U, 0U, 1U, 115U}  /* PLL_OUT = 120MHz, VCO = 960MHz */
        },
        /* PLL_IN = 8MHz */
        {
            {0U, 0U, 2U, 1U, 115U}, /* PLL_OUT = 80MHz, VCO = 960MHz */
            {0U, 1U, 0U, 1U, 55U}   /* PLL_OUT = 120MHz, VCO = 960MHz */
        },
        /* PLL_IN = 12MHz */
        {
            {0U, 0U, 2U, 1U, 75U},  /* PLL_OUT = 80MHz, VCO = 960MHz */
            {0U, 1U, 0U, 1U, 35U}   /* PLL_OUT = 120MHz, VCO = 960MHz */
        },
        /* PLL_IN = 16MHz */
        {
            {0U, 0U, 2U, 1U, 55U},  /* PLL_OUT = 80MHz, VCO = 960MHz */
            {0U, 1U, 0U, 1U, 25U}   /* PLL_OUT = 120MHz, VCO = 960MHz */
        },
        /* PLL_IN = 20MHz */
        {
            {0U, 0U, 2U, 1U, 43U},  /* PLL_OUT = 80MHz, VCO = 960MHz */
            {0U, 1U, 0U, 1U, 19U}   /* PLL_OUT = 120MHz, VCO = 960MHz */
        },
        /* PLL_IN = 24MHz */
        {
            {0U, 0U, 2U, 1U, 35U},  /* PLL_OUT = 80MHz, VCO = 960MHz */
            {0U, 1U, 0U, 1U, 15U}   /* PLL_OUT = 120MHz, VCO = 960MHz */
        },
        /* PLL_IN = 30MHz */
        {
            {0U, 0U, 2U, 1U, 27U},  /* PLL_OUT = 80MHz, VCO = 960MHz */
            {0U, 1U, 0U, 1U, 11U}   /* PLL_OUT = 120MHz, VCO = 960MHz */
        },
    }

};
#else
static const pll_div_t s_pllDivConfig[VCO_FREQ_MAX][PLL_IN_MAX][PLL_OUT_MAX] = {
    /* prediv, vcodiv, postdiv1, postdiv2, fbdiv */
    /* VCO = 672MHz */
    {
        /* PLL_IN = 4MHz */
        {
            {0U, 0U, 6U, 2U, 163U}, /* PLL_OUT = 24MHz */
            {0U, 0U, 6U, 1U, 163U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 8MHz */
        {
            {1U, 0U, 6U, 2U, 163U}, /* PLL_OUT = 24MHz */
            {1U, 0U, 6U, 1U, 163U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 12MHz */
        {
            {2U, 0U, 6U, 2U, 163U}, /* PLL_OUT = 24MHz */
            {2U, 0U, 6U, 1U, 163U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 16MHz */
        {
            {3U, 0U, 6U, 2U, 163U}, /* PLL_OUT = 24MHz */
            {3U, 0U, 6U, 1U, 163U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 20MHz */
        {
            {4U, 0U, 6U, 2U, 163U}, /* PLL_OUT = 24MHz */
            {4U, 0U, 6U, 1U, 163U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 24MHz */
        {
            {5U, 0U, 6U, 2U, 163U}, /* PLL_OUT = 24MHz */
            {5U, 0U, 6U, 1U, 163U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 30MHz */
        {
            {4U, 0U, 6U, 2U, 107U}, /* PLL_OUT = 24MHz */
            {4U, 0U, 6U, 1U, 107U}  /* PLL_OUT = 48MHz */
        }
    },

    /* VCO = 576MHz */
    {
        /* PLL_IN = 4MHz */
        {
            {0U, 1U, 2U, 2U, 67U}, /* PLL_OUT = 24MHz */
            {0U, 1U, 2U, 1U, 67U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 8MHz */
        {
            {1U, 1U, 2U, 2U, 67U}, /* PLL_OUT = 24MHz */
            {1U, 1U, 2U, 1U, 67U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 12MHz */
        {
            {2U, 1U, 2U, 2U, 67U}, /* PLL_OUT = 24MHz */
            {2U, 1U, 2U, 1U, 67U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 16MHz */
        {
            {3U, 1U, 2U, 2U, 67U}, /* PLL_OUT = 24MHz */
            {3U, 1U, 2U, 1U, 67U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 20MHz */
        {
            {4U, 1U, 2U, 2U, 67U}, /* PLL_OUT = 24MHz */
            {4U, 1U, 2U, 1U, 67U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 24MHz */
        {
            {5U, 1U, 2U, 2U, 67U}, /* PLL_OUT = 24MHz */
            {5U, 1U, 2U, 1U, 67U}  /* PLL_OUT = 48MHz */
        },
        /* PLL_IN = 30MHz */
        {
            {4U, 1U, 2U, 2U, 43U}, /* PLL_OUT = 24MHz */
            {4U, 1U, 2U, 1U, 43U}  /* PLL_OUT = 48MHz */
        }
    }
};
#endif

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/

/*******************************************************************************
 *** Function Name : CLOCK_ConfigHSI
 *** Description   : Configure HSI_CLK
 ******************************************************************************/
static void CLOCK_ConfigHSI(const hsi_config_t* hsiConfig)
{
    CLOCK_REG_SetHsiEnableInStop(CLOCK, hsiConfig->enableInStop);
}

/*******************************************************************************
 *** Function Name : CLOCK_ConfigHSE
 *** Description   : Configure HSE_CLK
 ******************************************************************************/
static errcode_t CLOCK_ConfigHSE(const hse_config_t* hseConfig)
{
    errcode_t errCode = ERR_SUCCESS;
    volatile uint32_t count = 0U;

    s_hseFreq = hseConfig->freq;
    CLOCK_REG_SetLocEvent(CLOCK, hseConfig->locEvent);
    CLOCK_REG_SetXoscIbs(CLOCK, 1U);

    if (hseConfig->enable) {
        /* Normal mode, need wait ready flag */
        CLOCK_REG_SetHseEnable(CLOCK, true);
        CLOCK_REG_SetXoscBypassEnable(CLOCK, false);

        /* Wait HSE ready */
        while (count++ < HSE_READY_TIMEOUT) {
            if (CLOCK_REG_HseIsReady(CLOCK)) {
                break;
            }
        }
    } else {
        /* Bypass mode, no need ready flag */
        CLOCK_REG_SetHseEnable(CLOCK, false);
        CLOCK_REG_SetXoscBypassEnable(CLOCK, true);
    }

    if (count < HSE_READY_TIMEOUT) {
        CLOCK_REG_SetHseMonEnable(CLOCK, hseConfig->monEnable);
    } else {
        errCode = ERR_TIMEOUT;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : CLOCK_ConfigPLL
 *** Description   : Configure PLL
 ******************************************************************************/
static errcode_t CLOCK_ConfigPLL(const pll_config_t* pllConfig)
{
    errcode_t errCode = ERR_SUCCESS;
    volatile uint32_t count = 0U;

    /* Assure PLL is disabled before configuration */
    CLOCK_REG_SetPllLockMonitorEnable(CLOCK, false);
    CLOCK_REG_SetPllEnable(CLOCK, false);

    /* Configurations */
    CLOCK_REG_SetLolEvent(CLOCK, pllConfig->lolEvent);
    CLOCK_REG_SetPllRefClk(CLOCK, pllConfig->pllRefClk);
    CLOCK_REG_SetPllPreDiv(CLOCK, pllConfig->prediv);
    CLOCK_REG_SetPllFbDiv(CLOCK, pllConfig->fbdiv);
    CLOCK_REG_SetPllVcoDiv(CLOCK, s_vcodiv);
    CLOCK_REG_SetPllPostDiv1(CLOCK, pllConfig->postdiv1);
    CLOCK_REG_SetPllPostDiv2(CLOCK, pllConfig->postdiv2);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    CLOCK_REG_SetPllIcp(CLOCK, 0xCU);
    CLOCK_REG_SetPllLockEnable(CLOCK, true);
#else
    CLOCK_REG_SetPllIcp(CLOCK, 0xFU);
#endif
    CLOCK_REG_SetPllEnable(CLOCK, pllConfig->enable);

    /* Wait PLL ready */
    while (count++ < PLL_READY_TIMEOUT) {
        if (CLOCK_REG_PllIsReady(CLOCK)) {
            break;
        }
    }

    if (count < PLL_READY_TIMEOUT) {
        CLOCK_REG_SetPllLockMonitorEnable(CLOCK, pllConfig->lockMonEnable);
    } else {
        errCode = ERR_TIMEOUT;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : CLOCK_SetAdcClockSource
 *** Description   : Set ADC function clock source
 ******************************************************************************/
static void CLOCK_SetAdcClockSource(uint32_t instance, adc_clk_src_t adcClkSrc)
{
    if (instance == 0U) {
        CLOCK_REG_SetAdc0ClockSource(CLOCK, adcClkSrc);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    } else if (instance == 1U) {
        CLOCK_REG_SetAdc1ClockSource(CLOCK, adcClkSrc);
#endif
    }
}

/*******************************************************************************
 *** Function Name : CLOCK_REG_SetCan0ClockSource
 *** Description   : Set CAN function clock source
 ******************************************************************************/
static void CLOCK_SetCanClockSource(uint32_t instance, can_clk_src_t canClkSrc)
{
    if (instance == 0U) {
        CLOCK_REG_SetCan0ClockSource(CLOCK, canClkSrc);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    } else if (instance == 1U) {
        CLOCK_REG_SetCan1ClockSource(CLOCK, canClkSrc);
    } else if (instance == 1U) {
        CLOCK_REG_SetCan2ClockSource(CLOCK, canClkSrc);
#endif
    }
}

/*******************************************************************************
 *** Function Name : CLOCK_SetTimerClockSource
 *** Description   : Set Timer function clock source
 ******************************************************************************/
static void CLOCK_SetTimerClockSource(uint32_t instance, timer_clk_src_t timerClkSrc)
{
    if (instance == 0U) {
        CLOCK_REG_SetTimer0ClockSource(CLOCK, timerClkSrc);
    } else if (instance == 1U) {
        CLOCK_REG_SetTimer1ClockSource(CLOCK, timerClkSrc);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    } else if (instance == 2U) {
        CLOCK_REG_SetTimer2ClockSource(CLOCK, timerClkSrc);
    } else if (instance == 3U) {
        CLOCK_REG_SetTimer3ClockSource(CLOCK, timerClkSrc);
#endif
    }
}

/*******************************************************************************
 *** Function Name : CLOCK_SetCmpClockSource
 *** Description   : Set CMP function clock source
 ******************************************************************************/
static void CLOCK_SetCmpClockSource(uint32_t instance, cmp_clk_src_t cmpClkSrc)
{
    if (instance == 0U) {
        CLOCK_REG_SetCmp0ClockSource(CLOCK, cmpClkSrc);
    }
}

/*******************************************************************************
 *** Function Name : CLOCK_CalculateAhbClockPrevious
 *** Description   : Calculate AHB clock (MHz) only by user configurations
 ******************************************************************************/
static uint32_t CLOCK_CalculateAhbClockPrevious(const clock_config_t* config)
{
    uint32_t fclkIn = 0U;
    uint32_t sysFreq = 0U;
    uint32_t ahbDiv = 1U;
    uint32_t ahbFreq = 0U;

    if (config->sysClkSrc == SYS_CLK_SRC_HSI) {
        sysFreq = HSI_CLK_FREQ;
    } else if (config->sysClkSrc == SYS_CLK_SRC_HSE) {
        sysFreq = s_hseFreq;
    } else if (config->sysClkSrc == SYS_CLK_SRC_PLL) {
        if (config->pllConfig.pllRefClk == PLL_REF_CLK_SRC_HSI) {
            fclkIn = HSI_CLK_FREQ;
        } else if (config->pllConfig.pllRefClk == PLL_REF_CLK_SRC_HSE) {
            fclkIn = s_hseFreq;
        } else {
            fclkIn = HSI_CLK_FREQ;
        }

        fclkIn = fclkIn / (config->pllConfig.prediv + 1U);
        fclkIn = fclkIn * (config->pllConfig.fbdiv + 5U);
        fclkIn = fclkIn / (config->pllConfig.postdiv1 + 1U);
        fclkIn = fclkIn / (1U << config->pllConfig.postdiv2);

#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
        sysFreq = fclkIn >> 1U;
#else
        sysFreq = fclkIn;
#endif
    } else {
        sysFreq = HSI_CLK_FREQ;
    }

    if (config->ahbClkPrescaler == AHB_CLK_PRESCALER_1) {
        ahbDiv = 1U;
    } else if (config->ahbClkPrescaler == AHB_CLK_PRESCALER_2) {
        ahbDiv = 2U;
    } else if (config->ahbClkPrescaler == AHB_CLK_PRESCALER_4) {
        ahbDiv = 4U;
    } else if (config->ahbClkPrescaler == AHB_CLK_PRESCALER_8) {
        ahbDiv = 8U;
    }

    ahbFreq = sysFreq / ahbDiv;
    ahbFreq = ahbFreq / 1000000U;

    return ahbFreq;
}

/*******************************************************************************
 *** Function Name : CLOCK_CalculatePLLClock
 *** Description   : Calculate PLL clock (Hz) by the configurations
 ******************************************************************************/
static uint32_t CLOCK_CalculatePllClock(void)
{
    uint32_t fclkIn = 0U;
    uint32_t fclkOut = 0U;

    if (CLOCK_REG_GetPllRefClk(CLOCK) == PLL_REF_CLK_SRC_HSI) {
        fclkIn = HSI_CLK_FREQ;
    } else if (CLOCK_REG_GetPllRefClk(CLOCK) == PLL_REF_CLK_SRC_HSE) {
        fclkIn = s_hseFreq;
    }

    fclkIn = fclkIn / (CLOCK_REG_GetPllPreDiv(CLOCK) + 1U);
    fclkIn = fclkIn * (CLOCK_REG_GetPllFbDiv(CLOCK) + 5U);
    fclkOut = fclkIn / (CLOCK_REG_GetPllPostDiv1(CLOCK) + 1U);
    fclkOut = fclkOut / (1U << CLOCK_REG_GetPllPostDiv2(CLOCK));

    return fclkOut;
}

/*******************************************************************************
 *** Function Name : CLOCK_CalculateSystemClock
 *** Description   : Calculate system clock (Hz) by the configurations
 ******************************************************************************/
static uint32_t CLOCK_CalculateSystemClock(void)
{
    uint32_t sysFreq;

    if (CLOCK_REG_GetSystemClockSource(CLOCK) == SYS_CLK_SRC_HSI) {
        sysFreq = HSI_CLK_FREQ;
    } else if (CLOCK_REG_GetSystemClockSource(CLOCK) == SYS_CLK_SRC_HSE) {
        sysFreq = s_hseFreq;
    } else if (CLOCK_REG_GetSystemClockSource(CLOCK) == SYS_CLK_SRC_PLL) {
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
        sysFreq = (CLOCK_CalculatePllClock() >> 1U);
#else
        sysFreq = CLOCK_CalculatePllClock();
#endif
    } else {
        sysFreq = HSI_CLK_FREQ;
    }

    return sysFreq;
}

/*******************************************************************************
 *** Function Name : CLOCK_CalculateTimerClock
 *** Description   : Calculate Timer (Hz) by the configurations
 ******************************************************************************/
static uint32_t CLOCK_CalculateTimerClock(uint32_t instance)
{
    uint32_t freqLocal = 0U;

#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    uint32_t peri0Freq = 0U;

    peri0Freq = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
    peri0Freq = peri0Freq / CLOCK_REG_GetApbPrescaler(CLOCK);
    peri0Freq = peri0Freq / CLOCK_REG_GetPeri0Prescaler(CLOCK);
#else
    uint32_t apbFreq = 0U;

    apbFreq = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
    apbFreq = apbFreq / CLOCK_REG_GetApbPrescaler(CLOCK);
#endif

    if (instance == 0U) {
        if (CLOCK_REG_GetTimer0ClockSource(CLOCK) == TIMER_CLK_SRC_OFF) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetTimer0ClockSource(CLOCK) == TIMER_CLK_SRC_TIMERIN) {
            freqLocal = 0U;
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
        } else if (CLOCK_REG_GetTimer0ClockSource(CLOCK) == TIMER_CLK_SRC_PERI0) {
            freqLocal = peri0Freq;
#else
        } else if (CLOCK_REG_GetTimer0ClockSource(CLOCK) == TIMER_CLK_SRC_APB) {
            freqLocal = apbFreq;
#endif
        } else if (CLOCK_REG_GetTimer0ClockSource(CLOCK) == TIMER_CLK_SRC_LSI) {
            freqLocal = LSI_CLK_FREQ;
        }
    } else if (instance == 1U) {
        if (CLOCK_REG_GetTimer1ClockSource(CLOCK) == TIMER_CLK_SRC_OFF) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetTimer1ClockSource(CLOCK) == TIMER_CLK_SRC_TIMERIN) {
            freqLocal = 0U;
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
        } else if (CLOCK_REG_GetTimer1ClockSource(CLOCK) == TIMER_CLK_SRC_PERI0) {
            freqLocal = peri0Freq;
#else
        } else if (CLOCK_REG_GetTimer1ClockSource(CLOCK) == TIMER_CLK_SRC_APB) {
            freqLocal = apbFreq;
#endif
        } else if (CLOCK_REG_GetTimer1ClockSource(CLOCK) == TIMER_CLK_SRC_LSI) {
            freqLocal = LSI_CLK_FREQ;
        }
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    } else if (instance == 2U) {
        if (CLOCK_REG_GetTimer2ClockSource(CLOCK) == TIMER_CLK_SRC_OFF) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetTimer2ClockSource(CLOCK) == TIMER_CLK_SRC_TIMERIN) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetTimer2ClockSource(CLOCK) == TIMER_CLK_SRC_PERI0) {
            freqLocal = peri0Freq;
        } else if (CLOCK_REG_GetTimer2ClockSource(CLOCK) == TIMER_CLK_SRC_LSI) {
            freqLocal = LSI_CLK_FREQ;
        }
    } else if (instance == 3U) {
        if (CLOCK_REG_GetTimer3ClockSource(CLOCK) == TIMER_CLK_SRC_OFF) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetTimer3ClockSource(CLOCK) == TIMER_CLK_SRC_TIMERIN) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetTimer3ClockSource(CLOCK) == TIMER_CLK_SRC_PERI0) {
            freqLocal = peri0Freq;
        } else if (CLOCK_REG_GetTimer3ClockSource(CLOCK) == TIMER_CLK_SRC_LSI) {
            freqLocal = LSI_CLK_FREQ;
        }
#endif
    }

    return freqLocal;
}

/*******************************************************************************
 *** Function Name : CLOCK_CalculateAdcClock
 *** Description   : Calculate ADC (Hz) by the configurations
 ******************************************************************************/
static uint32_t CLOCK_CalculateAdcClock(uint32_t instance)
{
    uint32_t freqLocal = 0U;
    uint32_t peri1Freq = 0U;

#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    peri1Freq = CLOCK_CalculatePllClock() / CLOCK_REG_GetPeri1Prescaler(CLOCK);
#else
    peri1Freq = CLOCK_CalculateSystemClock() / CLOCK_REG_GetPeri1Prescaler(CLOCK);
#endif

    if (instance == 0U) {
        if (CLOCK_REG_GetAdc0ClockSource(CLOCK) == ADC_CLK_SRC_OFF) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetAdc0ClockSource(CLOCK) == ADC_CLK_SRC_PERI1) {
            freqLocal = peri1Freq;
        } else if (CLOCK_REG_GetAdc0ClockSource(CLOCK) == ADC_CLK_SRC_HSI) {
            freqLocal = HSI_CLK_FREQ;
        } else if (CLOCK_REG_GetAdc0ClockSource(CLOCK) == ADC_CLK_SRC_HSE) {
            freqLocal = s_hseFreq;
        }
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    } else if (instance == 1U) {
        if (CLOCK_REG_GetAdc1ClockSource(CLOCK) == ADC_CLK_SRC_OFF) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetAdc1ClockSource(CLOCK) == ADC_CLK_SRC_PERI1) {
            freqLocal = peri1Freq;
        } else if (CLOCK_REG_GetAdc1ClockSource(CLOCK) == ADC_CLK_SRC_HSI) {
            freqLocal = HSI_CLK_FREQ;
        } else if (CLOCK_REG_GetAdc1ClockSource(CLOCK) == ADC_CLK_SRC_HSE) {
            freqLocal = s_hseFreq;
        }
#endif
    }

    return freqLocal;
}

/*******************************************************************************
 *** Function Name : CLOCK_CalculateCanClock
 *** Description   : Calculate CAN (Hz) by the configurations
 ******************************************************************************/
static uint32_t CLOCK_CalculateCanClock(uint32_t instance)
{
    uint32_t freqLocal = 0U;

#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    uint32_t apbFreq = 0U;

    apbFreq = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
    apbFreq = apbFreq / CLOCK_REG_GetApbPrescaler(CLOCK);
#else
    uint32_t peri0Freq = 0U;

    peri0Freq = CLOCK_CalculateSystemClock() / CLOCK_REG_GetPeri0Prescaler(CLOCK);
#endif

    if (instance == 0U) {
        if (CLOCK_REG_GetCan0ClockSource(CLOCK) == CAN_CLK_SRC_OFF) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetCan0ClockSource(CLOCK) == CAN_CLK_SRC_HSE) {
            freqLocal = s_hseFreq;
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
        } else if (CLOCK_REG_GetCan0ClockSource(CLOCK) == CAN_CLK_SRC_APB) {
            freqLocal = apbFreq;
#else
        } else if (CLOCK_REG_GetCan0ClockSource(CLOCK) == CAN_CLK_SRC_PERI0) {
            freqLocal = peri0Freq;
#endif
        } else {
            freqLocal = 0U;
        }
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    } else if (instance == 1U) {
        if (CLOCK_REG_GetCan1ClockSource(CLOCK) == CAN_CLK_SRC_OFF) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetCan1ClockSource(CLOCK) == CAN_CLK_SRC_HSE) {
            freqLocal = s_hseFreq;
        } else if (CLOCK_REG_GetCan1ClockSource(CLOCK) == CAN_CLK_SRC_APB) {
            freqLocal = apbFreq;
        } else {
            freqLocal = 0U;
        }
    } else if (instance == 2U) {
        if (CLOCK_REG_GetCan2ClockSource(CLOCK) == CAN_CLK_SRC_OFF) {
            freqLocal = 0U;
        } else if (CLOCK_REG_GetCan2ClockSource(CLOCK) == CAN_CLK_SRC_HSE) {
            freqLocal = s_hseFreq;
        } else if (CLOCK_REG_GetCan2ClockSource(CLOCK) == CAN_CLK_SRC_APB) {
            freqLocal = apbFreq;
        } else {
            freqLocal = 0U;
        }
#endif
    }

    return freqLocal;
}

/*******************************************************************************
 *** Function Name : CLOCK_CalculateCmpClock
 *** Description   : Calculate CMP (Hz) by the configurations
 ******************************************************************************/
static uint32_t CLOCK_CalculateCmpClock(uint32_t instance)
{
    uint32_t freqLocal = 0U;
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    uint32_t peri0Freq = 0U;

    peri0Freq = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
    peri0Freq = peri0Freq / CLOCK_REG_GetApbPrescaler(CLOCK);
    peri0Freq = peri0Freq / CLOCK_REG_GetPeri0Prescaler(CLOCK);
#else
    uint32_t apbFreq = 0U;

    apbFreq = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
    apbFreq = apbFreq / CLOCK_REG_GetApbPrescaler(CLOCK);
#endif

    if (instance == 0U) {
        if (CLOCK_REG_GetCmp0ClockSource(CLOCK) == CMP_CLK_SRC_OFF) {
            freqLocal = 0U;
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
        } else if (CLOCK_REG_GetCmp0ClockSource(CLOCK) == CMP_CLK_SRC_PERI0) {
            freqLocal = peri0Freq;
#else
        } else if (CLOCK_REG_GetCmp0ClockSource(CLOCK) == CMP_CLK_SRC_APB) {
            freqLocal = apbFreq;
#endif
        } else if (CLOCK_REG_GetCmp0ClockSource(CLOCK) == CMP_CLK_SRC_LSI) {
            freqLocal = LSI_CLK_FREQ;
        } else {
            freqLocal = 0U;
        }
    }

    return freqLocal;
}

/*******************************************************************************
 *** Function Name : CLOCK_GetHseFreq
 *** Description   : Get HSE frequency by pll input clock
 ******************************************************************************/
static uint32_t CLOCK_GetHseFreq(pll_in_t pllIn)
{
    uint32_t freq = 0U;

    if (pllIn == PLL_IN_4M) {
        freq = 4000000U;
    } else if (pllIn == PLL_IN_8M) {
        freq = 8000000U;
    } else if (pllIn == PLL_IN_12M) {
        freq = 12000000U;
    } else if (pllIn == PLL_IN_16M) {
        freq = 16000000U;
    } else if (pllIn == PLL_IN_20M) {
        freq = 20000000U;
    } else if (pllIn == PLL_IN_24M) {
        freq = 24000000U;
    } else if (pllIn == PLL_IN_30M) {
        freq = 30000000U;
    } else {
        freq = 8000000U;
    }

    return freq;
}

/*******Code*******************************************************************/
/*******************************************************************************
 *** Function Name : CLOCK_GetDefaultConfig
 *** Description   : Get a default recommended clock configurations according to
 ***                 current input frequency and expected output frequency.
 ******************************************************************************/
void CLOCK_GetDefaultConfig(clock_config_t* config, pll_in_t pllIn, pll_out_t pllOut)
{
    ASSERT_PARAM(config != NULL);

    uint32_t i = 0U;
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    vco_freq_t vcoFreq = VCO_FREQ_960M;
#else
    vco_freq_t vcoFreq = VCO_FREQ_672M;
#endif
    pll_div_t pllDivConfig;

    /* HSI clock settings */
    config->hsiConfig.enableInStop = false;

    /* HSE clock settings */
    config->hseConfig.freq = CLOCK_GetHseFreq(pllIn);
    config->hseConfig.locEvent = LOC_GEN_RESET;
    config->hseConfig.monEnable = true;
    config->hseConfig.enable = true;

    /* PLL setting: reference clock is HSE (pllIn), PLL output is pllOut */
    pllDivConfig = s_pllDivConfig[vcoFreq][pllIn][pllOut];
    s_vcodiv = (uint16_t)pllDivConfig.vcodiv;

    config->pllConfig.lolEvent = LOL_GEN_RESET;
    config->pllConfig.pllRefClk = PLL_REF_CLK_SRC_HSE;
    config->pllConfig.prediv = (uint16_t)pllDivConfig.prediv;
    config->pllConfig.fbdiv = (uint16_t)pllDivConfig.fbdiv;
    config->pllConfig.postdiv1 = (uint16_t)pllDivConfig.postdiv1;
    config->pllConfig.postdiv2 = (uint16_t)pllDivConfig.postdiv2;
    config->pllConfig.lockMonEnable = true;
    config->pllConfig.enable = true;

    /* Configure prescalers of AHB, APB, PERI0, PERI1 */
    config->ahbClkPrescaler = AHB_CLK_PRESCALER_1;
    config->apbClkPrescaler = APB_CLK_PRESCALER_2;
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    config->peri0ClkPrescaler = PERI0_CLK_PRESCALER_2;
    config->peri1ClkPrescaler = PERI1_CLK_PRESCALER_10;
#else
    config->peri0ClkPrescaler = PERI0_CLK_PRESCALER_2;
    config->peri1ClkPrescaler = PERI1_CLK_PRESCALER_2;
#endif

    /* Configure PLL_CLK as system clock */
    config->sysClkSrc = SYS_CLK_SRC_PLL;

    /* Turn off function clocks defaultly */
    for (i = 0U; i < CONFIG_ADC_INSTANCE_COUNT; i++) {
        config->adcClkSrc[i] = ADC_CLK_SRC_OFF;
    }

    for (i = 0U; i < CONFIG_CAN_INSTANCE_COUNT; i++) {
        config->canClkSrc[i] = CAN_CLK_SRC_OFF;
    }

    for (i = 0U; i < CONFIG_TIMER_INSTANCE_COUNT; i++) {
        config->timerClkSrc[i] = TIMER_CLK_SRC_OFF;
    }

    for (i = 0U; i < CONFIG_CMP_INSTANCE_COUNT; i++) {
        config->cmpClkSrc[i] = CMP_CLK_SRC_OFF;
    }
}

/*******************************************************************************
 *** Function Name : CLOCK_GetCurrentConfig
 *** Description   : Get current clock configurations.
 ******************************************************************************/
void CLOCK_GetCurrentConfig(clock_config_t* config)
{
    ASSERT_PARAM(config != NULL);

    /* Get HSI configurations */
    config->hsiConfig.enableInStop = CLOCK_REG_GetHsiEnableInStop(CLOCK);

    /* Get HSE configurations */
    config->hseConfig.freq = s_hseFreq;
    config->hseConfig.locEvent = CLOCK_REG_GetLocEvent(CLOCK);
    config->hseConfig.monEnable = CLOCK_REG_GetHseMonEnable(CLOCK);
    config->hseConfig.enable = CLOCK_REG_GetHseEnable(CLOCK);

    /* Get PLL configurations */
    config->pllConfig.lolEvent = CLOCK_REG_GetLolEvent(CLOCK);
    config->pllConfig.pllRefClk = CLOCK_REG_GetPllRefClk(CLOCK);
    config->pllConfig.prediv = (uint16_t)CLOCK_REG_GetPllPreDiv(CLOCK);
    config->pllConfig.fbdiv = (uint16_t)CLOCK_REG_GetPllFbDiv(CLOCK);
    config->pllConfig.postdiv1 = (uint16_t)CLOCK_REG_GetPllPostDiv1(CLOCK);
    config->pllConfig.postdiv2 = (uint16_t)CLOCK_REG_GetPllPostDiv2(CLOCK);
    config->pllConfig.lockMonEnable = CLOCK_REG_GetPllLockMonitorEnable(CLOCK);
    config->pllConfig.enable = CLOCK_REG_GetPllEnable(CLOCK);

    /* Get prescalers */
    config->ahbClkPrescaler = CLOCK_REG_GetAhbPrescalerByEnum(CLOCK);
    config->apbClkPrescaler = CLOCK_REG_GetApbPrescalerByEnum(CLOCK);
    config->peri0ClkPrescaler = CLOCK_REG_GetPeri0PrescalerByEnum(CLOCK);
    config->peri1ClkPrescaler = CLOCK_REG_GetPeri1PrescalerByEnum(CLOCK);

    /* Get system clock source */
    config->sysClkSrc = CLOCK_REG_GetSystemClockSource(CLOCK);

    /* Get module clock sources */
    config->adcClkSrc[0] = CLOCK_REG_GetAdc0ClockSource(CLOCK);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    config->adcClkSrc[1] = CLOCK_REG_GetAdc1ClockSource(CLOCK);
#endif

    config->canClkSrc[0] = CLOCK_REG_GetCan0ClockSource(CLOCK);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    config->canClkSrc[1] = CLOCK_REG_GetCan1ClockSource(CLOCK);
    config->canClkSrc[2] = CLOCK_REG_GetCan2ClockSource(CLOCK);
#endif

    config->timerClkSrc[0] = CLOCK_REG_GetTimer0ClockSource(CLOCK);
    config->timerClkSrc[1] = CLOCK_REG_GetTimer1ClockSource(CLOCK);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    config->timerClkSrc[2] = CLOCK_REG_GetTimer2ClockSource(CLOCK);
    config->timerClkSrc[3] = CLOCK_REG_GetTimer3ClockSource(CLOCK);
#endif

    config->cmpClkSrc[0] = CLOCK_REG_GetCmp0ClockSource(CLOCK);
}

/*******************************************************************************
 *** Function Name : CLOCK_Init
 *** Description   : Initial clock settigs according to pre-defined structure.
 ******************************************************************************/
errcode_t CLOCK_Init(const clock_config_t* config)
{
    ASSERT_PARAM(config != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t i = 0U;
    uint32_t ahbClkInMHz = HSI_CLK_FREQ / 1000000U;
    cmp_clk_src_t cmpSrc[CONFIG_CMP_INSTANCE_COUNT];

    for (i = 0U; i < CONFIG_CMP_INSTANCE_COUNT; i++) {
        cmpSrc[i] = config->cmpClkSrc[i];
    }

#if (CONFIG_FUNCTION_SAFE == 1U)
    SAFETY_EnablePowerProtect(CONFIG_SAFETY_POWER_SENSITIVITY, CONFIG_VDD_REF);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    cmpSrc[0] = CMP_CLK_SRC_PERI0;
#else
    cmpSrc[0] = CMP_CLK_SRC_APB;
#endif
#endif

    /* Switch system clock to HSI first */
    CLOCK_REG_SetSystemClockSource(CLOCK, SYS_CLK_SRC_HSI);
    CLOCK_REG_SetFlashClock(ahbClkInMHz);

    /* System clock sources configure and initial */
    CLOCK_ConfigHSI(&(config->hsiConfig));
    if (config->sysClkSrc == SYS_CLK_SRC_HSE) {
        errCode = CLOCK_ConfigHSE(&(config->hseConfig));
    } else if (config->sysClkSrc == SYS_CLK_SRC_PLL) {
        if (config->pllConfig.pllRefClk == PLL_REF_CLK_SRC_HSE) {
            errCode = CLOCK_ConfigHSE(&(config->hseConfig));
        }

        if (errCode == ERR_SUCCESS) {
            errCode = CLOCK_ConfigPLL(&(config->pllConfig));
        }
    } else {
        /* Do nothing */
    }

    if (errCode == ERR_SUCCESS) {
        /* Prescalers configurations */
        CLOCK_REG_SetAhbPrescaler(CLOCK, config->ahbClkPrescaler);
        CLOCK_REG_SetApbPrescaler(CLOCK, config->apbClkPrescaler);
        CLOCK_REG_SetPeri0Prescaler(CLOCK, config->peri0ClkPrescaler);
        CLOCK_REG_SetPeri1Prescaler(CLOCK, config->peri1ClkPrescaler);

        /* System clock and Flash clock configuration.
           Flash clock must be greater than or equal to System clock. */
        /* Calculate destination Flash clock (AHB_CLK) */
        ahbClkInMHz = CLOCK_CalculateAhbClockPrevious(config);
        CLOCK_REG_SetFlashClock(ahbClkInMHz);
        CLOCK_REG_SetSystemClockSource(CLOCK, config->sysClkSrc);

        /* Module function clock source select */
        for (i = 0U; i < CONFIG_ADC_INSTANCE_COUNT; i++) {
            CLOCK_SetAdcClockSource(i, config->adcClkSrc[i]);
        }

        for (i = 0U; i < CONFIG_CAN_INSTANCE_COUNT; i++) {
            CLOCK_SetCanClockSource(i, config->canClkSrc[i]);
        }

        for (i = 0U; i < CONFIG_TIMER_INSTANCE_COUNT; i++) {
            CLOCK_SetTimerClockSource(i, config->timerClkSrc[i]);
        }

        for (i = 0U; i < CONFIG_CMP_INSTANCE_COUNT; i++) {
            CLOCK_SetCmpClockSource(i, cmpSrc[i]);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : CLOCK_Deinit
 *** Description   : Deinit clock and restore clock settings.
 ******************************************************************************/
void CLOCK_Deinit(void)
{
    uint32_t i = 0U;
    uint32_t ahbClkInMHz = HSI_CLK_FREQ / 1000000U;

    /* Change system clock to HSI */
    CLOCK_REG_SetSystemClockSource(CLOCK, SYS_CLK_SRC_HSI);
    CLOCK_REG_SetFlashClock(ahbClkInMHz);

    /* Reset Clock gating and soft reset registers */
    CLOCK_REG_SetAhbModuleSoftReset(CLOCK, 0U);
    CLOCK_REG_SetAhbModuleBusClockEnable(CLOCK, 0U);
    CLOCK_REG_SetApbModuleSoftReset(CLOCK, 0U);
    CLOCK_REG_SetApbModuleBusClockEnable(CLOCK, 0U);

    /* Reset module function clock register */
    for (i = 0U; i < CONFIG_ADC_INSTANCE_COUNT; i++) {
        CLOCK_SetAdcClockSource(i, ADC_CLK_SRC_OFF);
    }

    for (i = 0U; i < CONFIG_CAN_INSTANCE_COUNT; i++) {
        CLOCK_SetCanClockSource(i, CAN_CLK_SRC_OFF);
    }

    for (i = 0U; i < CONFIG_TIMER_INSTANCE_COUNT; i++) {
        CLOCK_SetTimerClockSource(i, TIMER_CLK_SRC_OFF);
    }

    for (i = 0U; i < CONFIG_CMP_INSTANCE_COUNT; i++) {
        CLOCK_SetCmpClockSource(i, CMP_CLK_SRC_OFF);
    }

    /* Reset PLL control register */
    CLOCK_REG_SetPllLockMonitorEnable(CLOCK, false);
    CLOCK_REG_SetPllEnable(CLOCK, false);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    CLOCK_REG_SetPllLockEnable(CLOCK, true);
#endif
    CLOCK_REG_SetPllRefClk(CLOCK, PLL_REF_CLK_SRC_HSI);
    CLOCK_REG_SetPllPreDiv(CLOCK, 0U);
    CLOCK_REG_SetPllFbDiv(CLOCK, 55U);
    CLOCK_REG_SetPllPostDiv1(CLOCK, 0U);
    CLOCK_REG_SetPllPostDiv2(CLOCK, 1U);
    CLOCK_REG_SetPllIcp(CLOCK, 0U);
    CLOCK_REG_SetPllVcoDiv(CLOCK, 0U);

    /* Reset XOSC control register */
    CLOCK_REG_SetHseMonEnable(CLOCK, false);
    CLOCK_REG_SetHseEnable(CLOCK, false);
    CLOCK_REG_SetXoscBypassEnable(CLOCK, false);
    CLOCK_REG_SetXoscIbs(CLOCK, 0U);

    /* Reset control register */
    CLOCK_REG_SetHsiEnableInStop(CLOCK, false);
    CLOCK_REG_SetAhbPrescaler(CLOCK, AHB_CLK_PRESCALER_1);
    CLOCK_REG_SetApbPrescaler(CLOCK, APB_CLK_PRESCALER_2);
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
    CLOCK_REG_SetPeri0Prescaler(CLOCK, PERI0_CLK_PRESCALER_2);
    CLOCK_REG_SetPeri1Prescaler(CLOCK, PERI1_CLK_PRESCALER_10);
#else
    CLOCK_REG_SetPeri0Prescaler(CLOCK, PERI0_CLK_PRESCALER_1);
    CLOCK_REG_SetPeri1Prescaler(CLOCK, PERI1_CLK_PRESCALER_1);
#endif
    CLOCK_REG_SetLocEvent(CLOCK, LOC_GEN_RESET);
    CLOCK_REG_SetLolEvent(CLOCK, LOL_GEN_RESET);
}

/*******************************************************************************
 *** Function Name : CLOCK_GetFreq
 *** Description   : Get the frequency of a certain clock by its name, unit is Hz.
 ******************************************************************************/
#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
errcode_t CLOCK_GetFreq(clk_names_t clockName, uint32_t* freq)
{
    errcode_t errCode = ERR_SUCCESS;
    uint32_t freqLocal = 0U;

    switch (clockName) {
        case CLK_HSI:
            freqLocal = HSI_CLK_FREQ;
            break;

        case CLK_LSI:
            freqLocal = LSI_CLK_FREQ;
            break;

        case CLK_HSE:
            freqLocal = s_hseFreq;
            break;

        case CLK_LSE:
            freqLocal = LSE_CLK_FREQ;
            break;

        case CLK_PLL:
            freqLocal = CLOCK_CalculatePllClock();
            break;

        case CLK_SYS:
            freqLocal = CLOCK_CalculateSystemClock();
            break;

        case CLK_AHB:
        case CLK_FLASH:
        case CLK_CRC:
        case CLK_DMA:
        case CLK_ERM:
        case CLK_HSM:
            freqLocal = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
            break;

        case CLK_APB:
        case CLK_SPWM0:
        case CLK_SPWM1:
        case CLK_SPWM2:
        case CLK_IPWM0:
        case CLK_IPWM1:
        case CLK_GPIO:
            freqLocal = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
            freqLocal = freqLocal / CLOCK_REG_GetApbPrescaler(CLOCK);
            break;

        case CLK_PERI0:
        case CLK_I2C0:
        case CLK_I2C1:
        case CLK_SPI0:
        case CLK_SPI1:
        case CLK_SPI2:
        case CLK_SPI3:
        case CLK_UART0:
        case CLK_UART1:
        case CLK_UART2:
        case CLK_UART3:
        case CLK_ICM:
            freqLocal = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
            freqLocal = freqLocal / CLOCK_REG_GetApbPrescaler(CLOCK);
            freqLocal = freqLocal / CLOCK_REG_GetPeri0Prescaler(CLOCK);
            break;

        case CLK_PERI1:
            freqLocal = CLOCK_CalculatePllClock() / CLOCK_REG_GetPeri1Prescaler(CLOCK);
            break;

        case CLK_TIMER0:
            freqLocal = CLOCK_CalculateTimerClock(0U);
            break;

        case CLK_TIMER1:
            freqLocal = CLOCK_CalculateTimerClock(1U);
            break;

        case CLK_TIMER2:
            freqLocal = CLOCK_CalculateTimerClock(2U);
            break;

        case CLK_TIMER3:
            freqLocal = CLOCK_CalculateTimerClock(3U);
            break;

        case CLK_ADC0:
            freqLocal = CLOCK_CalculateAdcClock(0U);
            break;

        case CLK_ADC1:
            freqLocal = CLOCK_CalculateAdcClock(1U);
            break;

        case CLK_CAN0:
            freqLocal = CLOCK_CalculateCanClock(0U);
            break;

        case CLK_CAN1:
            freqLocal = CLOCK_CalculateCanClock(1U);
            break;

        case CLK_CAN2:
            freqLocal = CLOCK_CalculateCanClock(2U);
            break;

        case CLK_CMP0:
            freqLocal = CLOCK_CalculateCmpClock(0U);
            break;

        default:
            freqLocal = 0U;
            errCode = ERR_ERROR;
            break;
    }

    *freq = freqLocal;

    return errCode;
}

#else
errcode_t CLOCK_GetFreq(clk_names_t clockName, uint32_t* freq)
{
    errcode_t errCode = ERR_SUCCESS;
    uint32_t freqLocal = 0U;

    switch (clockName) {
        case CLK_HSI:
            freqLocal = HSI_CLK_FREQ;
            break;

        case CLK_LSI:
            freqLocal = LSI_CLK_FREQ;
            break;

        case CLK_HSE:
            freqLocal = s_hseFreq;
            break;

        case CLK_LSE:
            freqLocal = LSE_CLK_FREQ;
            break;

        case CLK_PLL:
            freqLocal = CLOCK_CalculatePllClock();
            break;

        case CLK_SYS:
            freqLocal = CLOCK_CalculateSystemClock();
            break;

        case CLK_AHB:
        case CLK_FLASH:
        case CLK_SPWM0:
        case CLK_SPWM1:
        case CLK_IPWM0:
        case CLK_IPWM1:
        case CLK_CRC:
        case CLK_DMA:
        case CLK_HAU:
        case CLK_GPIO:
        case CLK_ERM:
            freqLocal = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
            break;

        case CLK_APB:
        case CLK_I2C0:
        case CLK_I2C1:
        case CLK_ICM:
        case CLK_SPI0:
        case CLK_SPI1:
        case CLK_UART0:
        case CLK_UART1:
        case CLK_UART2:
            freqLocal = CLOCK_CalculateSystemClock() / CLOCK_REG_GetAhbPrescaler(CLOCK);
            freqLocal = freqLocal / CLOCK_REG_GetApbPrescaler(CLOCK);
            break;

        case CLK_PERI0:
            freqLocal = CLOCK_CalculateSystemClock() / CLOCK_REG_GetPeri0Prescaler(CLOCK);
            break;

        case CLK_PERI1:
            freqLocal = CLOCK_CalculateSystemClock() / CLOCK_REG_GetPeri1Prescaler(CLOCK);
            break;

        case CLK_TIMER0:
            freqLocal = CLOCK_CalculateTimerClock(0U);
            break;

        case CLK_TIMER1:
            freqLocal = CLOCK_CalculateTimerClock(1U);
            break;

        case CLK_ADC0:
            freqLocal = CLOCK_CalculateAdcClock(0U);
            break;

        case CLK_CAN0:
            freqLocal = CLOCK_CalculateCanClock(0U);
            break;

        case CLK_CMP0:
            freqLocal = CLOCK_CalculateCmpClock(0U);
            break;

        default:
            freqLocal = 0U;
            errCode = ERR_ERROR;
            break;
    }

    *freq = freqLocal;

    return errCode;
}
#endif

/*******************************************************************************
 *** Function Name : CLOCK_SetSystemClockSource
 *** Description   : Configure settings for system clock sources: HSI, HSE, PLL.
 ******************************************************************************/
errcode_t CLOCK_SetSystemClockSource(clk_names_t sysClkSrc, const void* clkConfig)
{
    errcode_t errCode = ERR_SUCCESS;

    switch (sysClkSrc) {
        case CLK_HSI:
            CLOCK_ConfigHSI((const hsi_config_t*)clkConfig);
            break;

        case CLK_HSE:
            errCode = CLOCK_ConfigHSE((const hse_config_t*)clkConfig);
            break;

        case CLK_PLL:
            errCode = CLOCK_ConfigPLL((const pll_config_t*)clkConfig);
            break;

        default:
            errCode = ERR_UNSUPPORTED;
            break;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : CLOCK_SetHsiEnableInStop
 *** Description   : Set if HSI clock keep enabled in STOP mode.
 ******************************************************************************/
void CLOCK_SetHsiEnableInStop(bool enable)
{
    CLOCK_REG_SetHsiEnableInStop(CLOCK, enable);
}

/*******************************************************************************
 *** Function Name : CLOCK_SetMainClockPrescaler
 *** Description   : Configure prescaler value for main clocks: AHB, APB, PERI.
 ******************************************************************************/
errcode_t CLOCK_SetMainClockPrescaler(clk_names_t clkName, uint16_t prescaler)
{
    errcode_t errCode = ERR_SUCCESS;

    switch (clkName) {
        case CLK_AHB:
            CLOCK_REG_SetAhbPrescaler(CLOCK, (ahb_clk_prescaler_t)prescaler);
            break;

        case CLK_APB:
            CLOCK_REG_SetApbPrescaler(CLOCK, (apb_clk_prescaler_t)prescaler);
            break;

        case CLK_PERI0:
            CLOCK_REG_SetPeri0Prescaler(CLOCK, (peri0_clk_prescaler_t)prescaler);
            break;

        case CLK_PERI1:
            CLOCK_REG_SetPeri1Prescaler(CLOCK, (peri1_clk_prescaler_t)prescaler);
            break;

        default:
            errCode = ERR_UNSUPPORTED;
            break;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : CLOCK_SelectSystemClockSource
 *** Description   : Select clock source for system clock.
 ******************************************************************************/
void CLOCK_SelectSystemClockSource(clk_names_t sysClock, sys_clk_src_t sysClkSrc)
{
    ASSERT_PARAM(sysClock == CLK_SYS);

    CLOCK_REG_SetSystemClockSource(CLOCK, sysClkSrc);
}

/*******************************************************************************
 *** Function Name : CLOCK_SelectModuleClockSource
 *** Description   : Select clock source for module clocks.
 ******************************************************************************/
errcode_t CLOCK_SelectModuleClockSource(clk_names_t moduleClock, uint16_t moduleClkSrc)
{
    errcode_t errCode = ERR_SUCCESS;

    switch (moduleClock) {
        case CLK_ADC0:
            CLOCK_REG_SetAdc0ClockSource(CLOCK, (adc_clk_src_t)moduleClkSrc);
            break;

#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
        case CLK_ADC1:
            CLOCK_REG_SetAdc1ClockSource(CLOCK, (adc_clk_src_t)moduleClkSrc);
            break;
#endif

        case CLK_CAN0:
            CLOCK_REG_SetCan0ClockSource(CLOCK, (can_clk_src_t)moduleClkSrc);
            break;

#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
        case CLK_CAN1:
            CLOCK_REG_SetCan1ClockSource(CLOCK, (can_clk_src_t)moduleClkSrc);
            break;

        case CLK_CAN2:
            CLOCK_REG_SetCan2ClockSource(CLOCK, (can_clk_src_t)moduleClkSrc);
            break;
#endif

        case CLK_TIMER0:
            CLOCK_REG_SetTimer0ClockSource(CLOCK, (timer_clk_src_t)moduleClkSrc);
            break;

        case CLK_TIMER1:
            CLOCK_REG_SetTimer1ClockSource(CLOCK, (timer_clk_src_t)moduleClkSrc);
            break;

#if (CONFIG_CLOCK_NEW_FEATURES == 1U)
        case CLK_TIMER2:
            CLOCK_REG_SetTimer2ClockSource(CLOCK, (timer_clk_src_t)moduleClkSrc);
            break;

        case CLK_TIMER3:
            CLOCK_REG_SetTimer3ClockSource(CLOCK, (timer_clk_src_t)moduleClkSrc);
            break;
#endif

        case CLK_CMP0:
            CLOCK_REG_SetCmp0ClockSource(CLOCK, (cmp_clk_src_t)moduleClkSrc);
            break;

        default:
            errCode = ERR_UNSUPPORTED;
            break;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : CLOCK_SetModuleBusClockEnable
 *** Description   : Enable or disable bus clock for modules.
 ***                 Modules are divided into APB group and AHB group according
 ***                 to their bus clock. I2C0 ~ ICM belong to APB group, DMA ~
 ***                 ERM belong to AHB group.
 ******************************************************************************/
void CLOCK_SetModuleBusClockEnable(module_names_t moduleName, bool enable)
{
    ASSERT_PARAM(moduleName < MODULE_MAX);

    uint32_t moduleNameLocal = (uint32_t)moduleName;
    uint32_t modulePos = 0U;
    uint32_t moduleClockGate = 0U;

    if (moduleNameLocal < CLOCK_REG_WIDTH) {
        modulePos = moduleNameLocal;
        moduleClockGate = CLOCK_REG_GetApbModuleBusClock(CLOCK);
    } else {
        modulePos = moduleNameLocal - CLOCK_REG_WIDTH;
        moduleClockGate = CLOCK_REG_GetAhbModuleBusClock(CLOCK);
    }

    if (enable) {
        moduleClockGate |= (uint32_t)(1U << modulePos);
    } else {
        moduleClockGate &= ~((uint32_t)(1U << modulePos));
    }

    if (moduleNameLocal < CLOCK_REG_WIDTH) {
        CLOCK_REG_SetApbModuleBusClockEnable(CLOCK, moduleClockGate);
    } else {
        CLOCK_REG_SetAhbModuleBusClockEnable(CLOCK, moduleClockGate);
    }
}

/*******************************************************************************
 *** Function Name : CLOCK_SetModuleSoftResetState
 *** Description   : Set soft reset for modules.
 ***                 Modules are divided into APB group and AHB group according
 ***                 to their bus clock. I2C0 ~ ICM belong to APB group, DMA ~
 ***                 ERM belong to AHB group.
 ******************************************************************************/
void CLOCK_SetModuleSoftResetState(module_names_t moduleName, reset_state_t resetState)
{
    ASSERT_PARAM(moduleName < MODULE_MAX);

    uint32_t moduleNameLocal = (uint32_t)moduleName;
    uint32_t modulePos = 0U;
    uint32_t moduleSoftReset = 0U;

    if (moduleNameLocal < CLOCK_REG_WIDTH) {
        modulePos = moduleNameLocal;
        moduleSoftReset = CLOCK_REG_GetApbModuleSoftReset(CLOCK);
    } else {
        modulePos = moduleNameLocal - CLOCK_REG_WIDTH;
        moduleSoftReset = CLOCK_REG_GetAhbModuleSoftReset(CLOCK);
    }

    if (resetState == RESET_ACTIVE) {
        moduleSoftReset &= ~((uint32_t)(1U << modulePos));
    } else {
        moduleSoftReset |= (uint32_t)(1U << modulePos);
    }

    if (moduleNameLocal < CLOCK_REG_WIDTH) {
        CLOCK_REG_SetApbModuleSoftReset(CLOCK, moduleSoftReset);
    } else {
        CLOCK_REG_SetAhbModuleSoftReset(CLOCK, moduleSoftReset);
    }
}

/*******************************************************************************
 *** Function Name : CLOCK_ClearLocFlag
 *** Description   : Clear HSE loss of clock flag.
 ******************************************************************************/
void CLOCK_ClearLocFlag(void)
{
    CLOCK_REG_ClearLocFlag(CLOCK);
}

/*******************************************************************************
 *** Function Name : CLOCK_ClearLolFlag
 *** Description   : Clear PLL loss of lock flag.
 ******************************************************************************/
void CLOCK_ClearLolFlag(void)
{
    CLOCK_REG_ClearLolFlag(CLOCK);
}

/*******EOF********************************************************************/

